package tool.zyk.net;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.DefaultEventExecutor;
import io.netty.util.concurrent.DefaultPromise;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import tool.zyk.configuration.SimpleRpcConfiguration;
import tool.zyk.protocol.ProtocolB2MDecoder;
import tool.zyk.protocol.ProtocolM2BEncoder;
import tool.zyk.protocol.ProtocolRequestM2MEncoder;
import tool.zyk.protocol.ProtocolResponseM2MDecoder;
import tool.zyk.stardand.RpcRequest;
import tool.zyk.stardand.RpcResponse;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * author: zyk
 * datetime: 2024/1/29 20:48
 * describe:
 */
@Slf4j
public class NioClient {

    private final SimpleRpcConfiguration simpleRpcConfiguration;

    public NioClient(SimpleRpcConfiguration simpleRpcConfiguration) {
        this.simpleRpcConfiguration = simpleRpcConfiguration;
    }

    @SneakyThrows
    public RpcResponse getResult(String ip, int port, RpcRequest rpcRequest) {
        NioEventLoopGroup executors = new NioEventLoopGroup();

        DefaultEventExecutor eventExecutors = new DefaultEventExecutor();
        DefaultPromise<RpcResponse> rpcResponseDefaultPromise = new DefaultPromise<>(eventExecutors);

        CompletableFuture.runAsync(() -> {
            try {

                ChannelFuture channelFuture = new Bootstrap()
                        .group(executors)
                        .channel(NioSocketChannel.class)
                        .handler(new ChannelInitializer<Channel>() {
                            @Override
                            protected void initChannel(Channel channel) throws Exception {
                                channel.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                                channel.pipeline().addLast(new ProtocolB2MDecoder());
                                channel.pipeline().addLast(new ProtocolResponseM2MDecoder(rpcResponseDefaultPromise));

                                channel.pipeline().addLast(new ProtocolM2BEncoder());
                                channel.pipeline().addLast(new ProtocolRequestM2MEncoder(simpleRpcConfiguration));
                            }
                        })
                        .connect(ip, port);
                Channel channel = channelFuture.sync().channel();
                log.info("connect success..");
                channel.writeAndFlush(rpcRequest);
                channel.closeFuture().sync();
            } catch (Exception e) {
                StringWriter stringWriter = new StringWriter();
                PrintWriter printWriter = new PrintWriter(stringWriter);
                e.printStackTrace(printWriter);
                String string = stringWriter.toString();
                log.error("异步调用出错了:\r\n{}", string);
            } finally {
                executors.shutdownGracefully();
            }
        });
        return rpcResponseDefaultPromise.get(simpleRpcConfiguration.getTimeWaitMill(), TimeUnit.MILLISECONDS);
    }
}
